package com.sinosoft.vaccinetoai.utils.h3cai;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.sinosoft.vaccinetoai.dao.redis.mapper.IRedisBaseDao;
import com.sinosoft.vaccinetoai.utils.h3cai.model.H3cAlApiCommonResult;
import com.sinosoft.vaccinetoai.utils.h3cai.model.H3cAlApiUserToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustAllStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;

/**
 * H3cAlApi接口 工具
 * @author xzh
 * @date 2023-10-15 15:53
 * @since 1.0.3
 */
@Slf4j
@EnableConfigurationProperties({ H3cAlApiProperties.class })
@Configuration
public class H3cAlApiUtil {

    @Autowired
    private IRedisBaseDao redisBaseDao;

    @Autowired
    private H3cAlApiProperties properties;

    // 异步响应线程池
    public static final ExecutorService ASYNC_STREAM_RESPONSE_EXECUTOR_SERVICE = ThreadUtil.newExecutor(3, Integer.MAX_VALUE);

    /**
     * 获取用户Token
     * @param isForceRefresh 标识是否强制刷新
     * @param version 当前版本
     * @return {@link H3cAlApiUserToken }
     * @throws Exception
     * @author xzh
     * @since 1.0.3
     */
    public H3cAlApiUserToken getUserToken(boolean isForceRefresh, long version) throws Exception {
        H3cAlApiUserToken userToken = null;

        /**
         * 调用获取
         * 失败则休眠重试
         */
        for(int i = 1; i <= 5; i++) {
            H3cAlApiUserToken userTokenTemp = null;
            try {
                userTokenTemp = doGetUserToken(isForceRefresh, version);
            }catch (Exception e) {
                log.error("获取H3cAlApi接口用户Token出现异常", e);
            }
            userToken = userTokenTemp;
            // 失败则休眠
            if(null == userToken) {
                Thread.sleep(300);
            }else {
                break;
            }
        }

        return userToken;
    }

    /**
     * 获取用户Token
     * @param isForceRefresh 标识是否强制刷新
     * @param version 当前版本
     * @return {@link H3cAlApiUserToken }
     * @throws Exception
     * @author xzh
     * @since 1.0.3
     */
    public H3cAlApiUserToken doGetUserToken(boolean isForceRefresh, long version) throws Exception {
        String clientId = properties.getClientId();
        H3cAlApiUserToken userToken = null;

        /**
         * 先从缓存中获取
         * 为空则重新获取
         */
        String userTokenStr = redisBaseDao.getString(H3cAlApiUserToken.REDIS_CACHE_KEY_PREFIX + clientId);
        if(!StrUtil.isEmpty(userTokenStr)) {
            userToken = JSONUtil.toBean(userTokenStr, H3cAlApiUserToken.class);
        }
        if(null == userToken || (isForceRefresh && (0 == version || Long.valueOf(version).equals(userToken.getVersion())))) {
            Boolean lock = redisBaseDao.setStringIfAbsent(H3cAlApiUserToken.REDIS_REFRESH_KEY_PREFIX + clientId, "1", (10 * 60 * 1000L));
            if(lock) {
                userToken = doRefreshUserToken();
                redisBaseDao.delete(H3cAlApiUserToken.REDIS_REFRESH_KEY_PREFIX + clientId);
            }
        }

        log.info("获取H3cAlApi接口用户Token <-> {}", userToken);
        return userToken;
    }

    /**
     * 刷新用户Token
     * @return {@link H3cAlApiUserToken }
     * @throws Exception
     * @author xzh
     * @since 1.0.3
     */
    private H3cAlApiUserToken doRefreshUserToken() throws Exception {

        H3cAlApiUserToken userToken = null;

        /**
         * 组装参数
         */
        Map<String, Object> params = new HashMap<>();
        params.put("grant_type","client_credentials");
        params.put("client_id",properties.getClientId());
        params.put("client_secret",properties.getClientKey());

        /**
         * 发送请求
         * 解析响应，缓存用户Token
         */
        HttpResponse httpResponse = commonReqApi("/api/hub/open/v1/auth/token", null, params, null, "POST", null);
        String body = EntityUtils.toString(httpResponse.getEntity());
        log.info("刷新H3cAlApi接口用户Token <->  status:{}, res:{}", httpResponse.getStatusLine().getStatusCode(), body);
        H3cAlApiCommonResult result = null;
        if(200 == httpResponse.getStatusLine().getStatusCode() && (result = JSONUtil.toBean(body, H3cAlApiCommonResult.class)).isSuccess()) {
            JSONObject data = JSONUtil.parseObj(result.getData());
            userToken = new H3cAlApiUserToken();

            userToken.setAccessToken(data.getStr("access_token"));
            // 过期时间 毫秒
            long expiresIn = data.getLong("expire_time");
            // 提前过期以触发提前刷新
            expiresIn -= 30 * 1000;
            userToken.setExpiresIn(expiresIn);
            // 版本号
            long version = redisBaseDao.incrby(H3cAlApiUserToken.REDIS_VERSION_KEY_PREFIX + properties.getClientId(), 1);
            userToken.setVersion(version);
            // 缓存
            redisBaseDao.setString(H3cAlApiUserToken.REDIS_CACHE_KEY_PREFIX + properties.getClientId(), JSONUtil.toJsonStr(userToken), expiresIn);
        }

        return userToken;
    }

    /**
     * 通用请求API
     * @param apiUri 请求接口
     * @param authorization 鉴权Token
     * @param entityParams 请求体参数
     * @param formParams Form参数
     * @param method 请求方法 POST（默认） GET等
     * @param contentType 内容类型 默认application/json
     * @return {@link HttpResponse }
     * @author xzh
     * @since 1.0.3
     */
    public HttpResponse commonReqApi(String apiUri, String authorization, Object entityParams, Map<String, Object> formParams, String method, String contentType) throws Exception {
        /**
         * 构建请求
         * 请求地址、方法、内容类型、请求头、请求参数、请求体
         */
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                SSLContexts.custom().loadTrustMaterial(null, new TrustAllStrategy()).build(),
                NoopHostnameVerifier.INSTANCE
        );
        // 客户端
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        // 请求方式
        HttpRequestBase httpRequest = null;
        if(null == method) method = "POST";
        method = method.toUpperCase();
        switch (method) {
            case "GET":
                httpRequest = new HttpGet();
                break;
            case "PUT":
                httpRequest = new HttpPut();
                break;
            case "POST":
            default:
                httpRequest = new HttpPost();
                break;
        }
        // 请求头 鉴权
        if(!StrUtil.isEmpty(authorization)) {
            httpRequest.addHeader("access_token", authorization);
        }
        // 请求地址
        String url = properties.getApiHost() + apiUri;
        if(CollectionUtil.isNotEmpty(formParams)) {
            String urlParams = HttpUtil.toParams(formParams);
            url += "?" + urlParams;
        }
        httpRequest.setURI(URI.create(url));
        // 请求体
        if(null == contentType) contentType = "application/json";
        if(null != entityParams) {
            String body = null;
            if(!(entityParams instanceof String)) {
                body = JSONUtil.toJsonStr(entityParams);
            }else {
                body = (String) entityParams;
            }
            HttpEntity reqEntity = new StringEntity(body, ContentType.create(contentType, "UTF-8"));
            ((HttpEntityEnclosingRequestBase) httpRequest).setEntity(reqEntity);
        }

        /**
         * 发送请求
         */
        return httpClient.execute(httpRequest);

        /*String url = properties.getApiHost() + apiUri;
        if(null == method) method = Method.POST;
        if(null == contentType) contentType = ContentType.JSON;
        HttpRequest httpRequest = HttpRequest.of(url).method(method).contentType(contentType.getValue());
        if(!StrUtil.isEmpty(authorization)) {
            httpRequest.header("Authorization", authorization);
        }
        httpRequest.form(formParams);
        if(null != entityParams) {
            String body = null;
            if(!(entityParams instanceof String)) {
                body = JSONUtil.toJsonStr(entityParams);
            }else {
                body = (String) entityParams;
            }
            httpRequest.body(body);
        }*/

        /**
         * 发送请求
         */
        //return httpRequest.execute();
    }

    /**
     * 对话聊天接口
     * @param uri 请求地址
     * @param entityParams 参数
     * @param consumer 消息回调
     * @param isStream 是否流式
     * @throws Exception
     * @author xzh
     * @since 1.0.3
     */
    public void chatReqApi(String uri,Object entityParams, BiConsumer<String, Integer> consumer, boolean isStream) throws Exception {
        /**
         * 获取用户Token
         */
        H3cAlApiUserToken userToken = getUserToken(false, 0);
        if(null == userToken || StrUtil.isEmpty(userToken.getAccessToken())) {
            if(null != consumer) consumer.accept(null, 0);
            return;
        }
        log.debug("H3cAlApi交互请求体：{}",entityParams.toString());
        /**
         * 请求接口
         * 获取返回数据
         */
        HttpResponse httpResponse = commonReqApi(uri, userToken.getAccessToken(), entityParams, null, null, null);
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        HttpEntity resEntity = httpResponse.getEntity();
        log.debug("H3cAlApi状态码：{}",statusCode);
        if(200 == statusCode) {
            if(isStream) {
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resEntity.getContent(), "UTF-8"));
                String line;
                while(null != (line = bufferedReader.readLine())) {
                    log.info("H3cAlApi对话聊天 <-> {}", line);
                    if(null != consumer) consumer.accept(line, 1);
                }
                EntityUtils.consume(resEntity);
            }else {
                consumer.accept(EntityUtils.toString(resEntity), 1);
            }
        }
        if(null != consumer) consumer.accept(null, 2);
    }




}
